using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using Verse;
using Verse.Sound;
using Verse.AI;
using Verse.AI.Group;

namespace RimWorld{
public class Building_Door : Building
{
	//Links
	public CompPowerTrader	powerComp;

	//Working vars - saved
	private bool 			openInt = false;
	private bool			holdOpenInt = false;		//Player has configured door to be/stay open
	private bool			freePassageUntilClosed = false;
	private int				lastFriendlyTouchTick = -9999;

	//Working vars - unsaved
	protected int 			ticksUntilClose = 0;
	protected int 			visualTicksOpen = 0;

	//Constants
	private const float		BaseDoorOpenTicks = 45;
	private const int		AutomaticCloseDelayTicks = 60;
	private const int		ApproachCloseDelayTicks = 300;	//Extra long in case pawn is very slow; don't want door to close before they arrive
	private const float		VisualDoorOffsetStart = 0.0f;
	private const float		VisualDoorOffsetEnd = 0.45f;

	//Properties	
	public bool Open{get{return openInt;}}
	public bool HoldOpen{get{return holdOpenInt;}}
	public bool FreePassage
	{
		get
		{
			return openInt && (holdOpenInt || freePassageUntilClosed || BlockedOpenLongTerm);
		}
	}
	public bool BlockedOpenMomentary
	{
		get
		{
			var thingList = Position.GetThingList(Map);
			for( int i=0; i<thingList.Count; i++ )
			{
				var t = thingList[i];
				if( t.def.category == ThingCategory.Item
					|| t.def.category == ThingCategory.Pawn)
					return true;
			}

			return false;
		}
	}
	public bool BlockedOpenLongTerm
	{
		get
		{
			var thingList = Position.GetThingList(Map);
			for( int i=0; i<thingList.Count; i++ )
			{
				var t = thingList[i];
				if( t.def.category == ThingCategory.Item )
				{
					return true;
				}
			}

			return false;
		}
	}
	public bool DoorPowerOn
	{
		get
		{
			return powerComp != null && powerComp.PowerOn;
		}
	}
	public bool SlowsPawns
	{
		get
		{
			return !DoorPowerOn || TicksToOpenNow > 20;
		}
	}
	public int TicksToOpenNow
	{
		get
		{
			float ticks = BaseDoorOpenTicks / this.GetStatValue( StatDefOf.DoorOpenSpeed );

			if( DoorPowerOn )
				ticks *= 0.25f;

			return Mathf.RoundToInt(ticks);
		}
	}
	private int VisualTicksToOpen
	{
		get
		{
			//return Mathf.RoundToInt(TicksToOpenNow * 0.85f);
			return TicksToOpenNow;
		}
	}


	public override void PostMake()
	{
		base.PostMake();
		powerComp = GetComp<CompPowerTrader>();
	}

	public override void SpawnSetup(Map map)
	{
		base.SpawnSetup(map);
		powerComp = GetComp<CompPowerTrader>();
	}

	public override void ExposeData()
	{
		base.ExposeData();
		Scribe_Values.LookValue(ref openInt, "open", defaultValue: false);
		Scribe_Values.LookValue(ref holdOpenInt, "holdOpen", defaultValue: false);
		Scribe_Values.LookValue(ref freePassageUntilClosed, "freePassageUntilClosed", defaultValue: false );
		Scribe_Values.LookValue(ref lastFriendlyTouchTick, "lastFriendlyTouchTick" );

		if( Scribe.mode == LoadSaveMode.LoadingVars )
			if( openInt )
				visualTicksOpen = VisualTicksToOpen;
	}

	public override void Tick()
	{
		base.Tick ();

		if( !openInt )
		{
			//Visual - slide door closed
			if( visualTicksOpen > 0 )
				visualTicksOpen--;

			//Equalize temperatures
			if( (Find.TickManager.TicksGame + thingIDNumber.HashOffset()) % TemperatureTuning.Door_TempEqualizeIntervalClosed == 0 )
				GenTemperature.EqualizeTemperaturesThroughBuilding(this, TemperatureTuning.Door_TempEqualizeRate, twoWay: false);
		}
		else if( openInt )
		{
			//Visual - slide door open
			if( visualTicksOpen < VisualTicksToOpen )
				visualTicksOpen++;

			//Count down to closing
			if( !holdOpenInt )
			{
				if( Map.thingGrid.CellContains( Position, ThingCategory.Pawn ) )
					ticksUntilClose = AutomaticCloseDelayTicks;
				else
				{
					ticksUntilClose--;

					//If the power is on and we were touched recently, close automatically
					if( DoorPowerOn && ticksUntilClose <= 0 && Find.TickManager.TicksGame < lastFriendlyTouchTick + 200 )
						DoorTryClose();
				}
			}

			//Equalize temperatures
			if( (Find.TickManager.TicksGame + thingIDNumber.HashOffset()) % TemperatureTuning.Door_TempEqualizeIntervalOpen == 0 )
				GenTemperature.EqualizeTemperaturesThroughBuilding(this, TemperatureTuning.Door_TempEqualizeRate, twoWay: false);
		}
	}

	public void FriendlyTouched()
	{
		lastFriendlyTouchTick = Find.TickManager.TicksGame;
	}

	

	public void Notify_PawnApproaching( Pawn p )
	{
		//Open automatically before pawn arrives
		if( PawnCanOpen(p) && !SlowsPawns )
			DoorOpen( ApproachCloseDelayTicks );
	}

	/// <summary>
	/// Returns whether p can physically pass through the door without bashing.
	/// </summary>
	public bool CanPhysicallyPass( Pawn p )
	{
		return FreePassage || PawnCanOpen(p);
	}

	/// <summary>
	/// Returns whether p can open the door without bashing.
	/// </summary>
	public virtual bool PawnCanOpen( Pawn p )
	{
		//Animals *are* allowed to open doors
	//	if( p.RaceProps.Animal )
	//		return false;

		var lord = p.GetLord();
		if( lord != null && lord.LordJob != null && lord.LordJob.CanOpenAnyDoor(p) )
			return true;

		if( Faction == null )
			return true;

		return GenAI.MachinesLike(Faction, p);
	}
	
	public override bool BlocksPawn( Pawn p )
	{
		if( openInt )
			return false;
		else
			return !PawnCanOpen(p);
	}

	protected void DoorOpen(int ticksToClose = AutomaticCloseDelayTicks)
	{
		ticksUntilClose = ticksToClose;

		if( !openInt )
		{
			openInt = true;

			if( holdOpenInt )
			{
				freePassageUntilClosed = true;

				if( Spawned )
					Map.reachability.ClearCache();
			}

			if( DoorPowerOn )
				def.building.soundDoorOpenPowered.PlayOneShot(new TargetInfo(Position, Map));
			else
				def.building.soundDoorOpenManual.PlayOneShot(new TargetInfo(Position, Map));
		}
	}


	protected void DoorTryClose()
	{
		if( holdOpenInt || BlockedOpenMomentary )
			return;

		openInt = false;

		if( freePassageUntilClosed )
		{
			freePassageUntilClosed = false;

			if( Spawned )
				Map.reachability.ClearCache();
		}

		if( DoorPowerOn )
			def.building.soundDoorClosePowered.PlayOneShot(new TargetInfo(Position, Map));
		else
			def.building.soundDoorCloseManual.PlayOneShot(new TargetInfo(Position, Map));
	}

		
	public void StartManualOpenBy( Pawn opener )
	{
		DoorOpen();
	}

	public void StartManualCloseBy( Pawn closer )
	{
		DoorTryClose();
	}

	public override void Draw()
	{
		//Note: It's a bit odd that I'm changing game variables in Draw
		//      but this is the easiest way to make this always look right even if
		//      conditions change while the game is paused.
		Rotation = DoorRotationAt(Position, Map);

		//Draw the two moving doors
		float pctOpen = Mathf.Clamp01((float)visualTicksOpen / (float)VisualTicksToOpen);	//Needs clamp for after game load		
		float offsetDist = VisualDoorOffsetStart + (VisualDoorOffsetEnd-VisualDoorOffsetStart)*pctOpen;	

		for( int i=0; i<2; i++ )
		{
			//Left, then right
			Vector3 offsetNormal = new Vector3();
			Mesh mesh;
			if( i == 0 )
			{
				offsetNormal = new Vector3(0,0,-1);
				mesh = MeshPool.plane10;
			}
			else
			{
				offsetNormal = new Vector3(0,0,1);
				mesh = MeshPool.plane10Flip;
			}
			

			//Work out move direction
			Rot4 openDir = Rotation;
			openDir.Rotate(RotationDirection.Clockwise);
			offsetNormal  = openDir.AsQuat * offsetNormal;

			//Position the door
			Vector3 doorPos =  DrawPos;
			doorPos.y = Altitudes.AltitudeFor(AltitudeLayer.DoorMoveable);
			doorPos += offsetNormal * offsetDist;
		
			//Draw!
			Graphics.DrawMesh(mesh, doorPos, Rotation.AsQuat, Graphic.MatAt(Rotation), 0 );
		}
			
		Comps_PostDraw();
	}


	private static int AlignQualityAgainst( IntVec3 c, Map map )
	{
		if( !c.InBounds(map) )
			return 0;

		//We align against anything unwalkthroughable and against blueprints for unwalkthroughable things
		if( !c.Walkable(map) )
			return 9;
			
		List<Thing> things = c.GetThingList(map);
		for(int i=0; i<things.Count; i++ )
		{
			Thing t = things[i];

			if( typeof(Building_Door).IsAssignableFrom(t.def.thingClass) )
				return 1;

			Thing blue = t as Blueprint;
			if( blue != null )
			{
				if( blue.def.entityDefToBuild.passability == Traversability.Impassable )
					return 9;
				if( typeof(Building_Door).IsAssignableFrom(t.def.thingClass) )
					return 1;
			}
		}
			
		return 0;		
	}

	public static Rot4 DoorRotationAt(IntVec3 loc, Map map)
	{
		int horVotes = 0;
		int verVotes = 0;

		horVotes += AlignQualityAgainst( loc + IntVec3.East, map );
		horVotes += AlignQualityAgainst( loc + IntVec3.West, map );
		verVotes += AlignQualityAgainst( loc + IntVec3.North, map );
		verVotes += AlignQualityAgainst( loc + IntVec3.South, map );

		if( horVotes >= verVotes )
			return Rot4.North;
		else
			return Rot4.East;
	}

	public void Notify_ItemSpawnedOrDespawnedOnTop( Thing t )
	{
		Map.reachability.ClearCache();
	}

	public override IEnumerable<Gizmo> GetGizmos()
	{
		foreach( var g in base.GetGizmos() )
		{
			yield return g;
		}

		if( Faction == Faction.OfPlayer )
		{
			Command_Toggle ro = new Command_Toggle();
			ro.defaultLabel = "CommandToggleDoorHoldOpen".Translate();
			ro.defaultDesc = "CommandToggleDoorHoldOpenDesc".Translate();
			ro.hotKey = KeyBindingDefOf.Misc3;
			ro.icon = TexCommand.HoldOpen;
			ro.isActive = ()=>holdOpenInt;
			ro.toggleAction = ()=>
			{
				holdOpenInt = !holdOpenInt;

				Map.reachability.ClearCache();

				if( Open && holdOpenInt )
					freePassageUntilClosed = true;
			};
			yield return ro;
		}
	}
}
}


